package cn.dengta.webapp.base.util;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;

import cn.dengta.common.util.*;
import cn.dengta.common.web.Client;
import cn.dengta.context.model.RedisKey;
import cn.dengta.context.model.WesKit;
import cn.dengta.context.web.ProxyRequest;
import cn.dengta.webapp.base.dto.AuditEnvConfigDTO;
import me.codeplayer.util.*;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * APP版本兼容 辅助工具类
 */
public abstract class AppVersions {

	/** 格式：<Client,<Weskit.name(),version>> */
	static final LazyCacheLoader<Map<Client, Map<String, String>>> CLIENT_VERSION_CACHE = new TimeBasedCacheLoader<>(EasyDate.MILLIS_OF_MINUTE * 10, AppVersions::getRedisVersion);
	/**  */
	static final LazyCacheLoader<Map<WesKit, AuditEnvConfigDTO>> WESKIT_VERSION_CACHE = new TimeBasedCacheLoader<>(EasyDate.MILLIS_OF_MINUTE, AppVersions::getAuditEnvConfigDTOData);
	private static final Logger log = LoggerFactory.getLogger(AppVersions.class);

	static transient StringRedisTemplate stringRedisTemplate;

	public static final Integer[]
			v_2_3_0 = { 2, 3, 0 },
			v_3_0_0 = { 3, 0, 0 },
			v_3_1_0 = { 3, 1, 0 },
			v_3_3_0 = { 3, 3, 0 },
			v_3_5_0 = { 3, 5, 0 },
			v_3_5_1 = { 3, 5, 1 },
			v_4_0_0 = { 4, 0, 0 },
			v_4_2_0 = { 4, 2, 0 },
			v_4_3_0 = { 4, 3, 0 },
			v_4_4_0 = { 4, 4, 0 },
			v_4_4_2 = { 4, 4, 2 },
			v_5_0_1 = { 5, 0, 1 },
			v_5_0_2 = { 5, 0, 2 },
			v_5_1_1 = { 5, 1, 1 },
			v_5_1_7 = { 5, 1, 7 },
			v_5_3_5 = { 5, 3, 5 }


	//
					;

	public static boolean lt(String current, Integer[] target) {
		return compare(current, target) < 0;
	}

	public static boolean le(String current, Integer[] target) {
		return compare(current, target) <= 0;
	}

	public static boolean ge(String current, Integer[] target) {
		return compare(current, target) >= 0;
	}

	public static boolean gt(String current, Integer[] target) {
		return compare(current, target) > 0;
	}

	public static boolean eq(String current, Integer[] target) {
		return compare(current, target) == 0;
	}

	/**
	 * 获取指定客户端类型当前正在审核中
	 */
	public static boolean isVerifyVersion(Client client, boolean def) {
		return isVerifyVersion(client, null, def);
	}

	/**
	 * 获取指定客户端类型当前正在审核中
	 */
	public static boolean isVerifyVersion(Client client, WesKit wesKit, boolean def) {
		if (client != null) {
			Map<String, String> clientMap = CLIENT_VERSION_CACHE.get().get(client);
			wesKit = def && wesKit == null ? WesKit.ofWesKit(false) : wesKit;
			return !X.isValid(clientMap) || wesKit == null || clientMap.containsKey(wesKit.name());
		}
		return false;
	}

	/**
	 * 检测当前请求客户端的APP 版本是否在上架审核中
	 */
	public static boolean inVerify(Long userId, @Nullable Client client, @Nullable String version) {
		return inVerify(userId, client, null, version);
	}

	public static boolean inIosVerify(Long userId, WesKit wesKit, @Nullable String version) {
		return inVerify(userId, Client.APP_IOS, wesKit, version);
	}

	public static boolean inVerify(Long userId, @Nullable Client client, WesKit wesKit, @Nullable String version) {
		wesKit = wesKit == null ? WesKit.ofWesKit() : wesKit;
		try {
			if (iosUserForChain(userId, client, wesKit, null, version)) {
				return true;
			}
		} catch (RuntimeException e) {
			log.error("校验IP出错", e);
		}
		if (StringUtil.notEmpty(version)) {
			final Map<Client, Map<String, String>> map = CLIENT_VERSION_CACHE.get();
			final Map<String, String> clientMap = map.get(client);
			if (X.isValid(clientMap)) {
				return version.equals(clientMap.get(wesKit.name()));
			}
		}
		return false;
	}

	public static boolean iosUserForChain(Long userId, Client client, WesKit wesKit, String ip, String version) {
		return iosUserForChain(userId, client, wesKit, ip, version, null);
	}

	public static boolean iosUserForChain(Long userId, Client client, WesKit wesKit, String ip, String version, Predicate<AuditEnvConfigDTO> preFun) {
		if (client == Client.APP_IOS && wesKit.isInterior()) {
			AuditEnvConfigDTO dto = WESKIT_VERSION_CACHE.get().get(wesKit);
			dto = dto == null ? AuditEnvConfigDTO.ofDef() : dto;
			if (Boolean.TRUE.equals(dto.getOutIp())) {
				if (userId != null) {
					StringRedisTemplate redisTemplate = stringRedisTemplate;
					if (redisTemplate == null) {
						stringRedisTemplate = redisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
					}
					final HashOperations<String, String, String> forHash = redisTemplate.opsForHash();
					ip = StringUtil.notEmpty(ip) ? ip : forHash.get(RedisKey.USER_IP, userId.toString());
				}
				if (StringUtil.notEmpty(ip) && !"127.0.0.1".equals(ip) && !IpHelper.fromChina(ip)) {
					return true;
				}
			}
			if (userId != null && preFun != null) {
				AuditEnvConfigDTO config = new AuditEnvConfigDTO();
				BeanUtil.copyProperties(dto, config);
				config.setUserId(userId);
				config.setClient(client);
				config.setWesKit(wesKit);
				config.setVersion(version);
				return preFun.test(config);
			}
		}
		return false;
	}

	/**
	 * 检测当前请求客户端的APP 版本是否在上架审核中
	 */
	public static boolean inVerify(@Nullable HttpServletRequest request) {
		return inVerify(request, null);
	}

	/**
	 * 检测当前请求客户端的APP 版本是否在上架审核中
	 *
	 * @param clientOnly 限定必须是指定客户端类型，否则返回 false。如果为 null 表示不限定
	 */
	public static boolean inVerify(final @Nullable HttpServletRequest request, final @Nullable Client clientOnly) {
		final Map<Client, Map<String, String>> map = CLIENT_VERSION_CACHE.get();
		if (!map.isEmpty() && request != null) {
			final WesKit wesKit = WesKit.ofAppId(ProxyRequest.getAppId(request));
			final Client current = Client.getClient(request);
			if (clientOnly == null || current == clientOnly) {
				Map<String, String> clientMap = map.get(current);
				if (X.isValid(clientMap) && clientMap.containsKey(wesKit.name())) {
					final String version = clientMap.get(wesKit.name());
					if (StringUtil.notEmpty(version)) {
						return version.equals(ProxyRequest.getVersion(ProxyRequest.getAppId(request)));
					}
				}
			}
		}
		return false;
	}

	/**
	 * 检测当前请求客户端的APP 版本是否在上架审核中
	 */
	public static boolean inVerify(ProxyRequest q) {
		return inVerify(q.getRequest());
	}

	/**
	 * 获取审核中的APP版本
	 */
	public static Map<Client, Map<String, String>> getRedisVersion() {
		StringRedisTemplate redisTemplate = stringRedisTemplate;
		if (redisTemplate == null) {
			stringRedisTemplate = redisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
		}
		final Map<String, String> iosVersion = redisTemplate.<String, String>opsForHash().entries(RedisKey.APP_VERSION + Client.APP_IOS.value);
		final Map<String, String> androidVersion = redisTemplate.<String, String>opsForHash().entries(RedisKey.APP_VERSION + Client.APP_ANDROID.value);
		final Map<Client, Map<String, String>> mapVersion = new HashMap<>(2);
		if (X.isValid(iosVersion)) {
			mapVersion.put(Client.APP_IOS, iosVersion);
		}
		if (X.isValid(androidVersion)) {
			mapVersion.put(Client.APP_ANDROID, androidVersion);
		}
		return mapVersion;
	}

	public static Map<WesKit, AuditEnvConfigDTO> getAuditEnvConfigDTOData() {
		StringRedisTemplate redisTemplate = stringRedisTemplate;
		if (redisTemplate == null) {
			stringRedisTemplate = redisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
		}
		final Map<WesKit, AuditEnvConfigDTO> map = new HashMap<>(8, 1);
		final HashOperations<String, String, String> forHash = redisTemplate.opsForHash();
		List<String> keys = Arrays.stream(WesKit.values()).filter(WesKit::isInterior).map(Enum::name).collect(Collectors.toList());
		List<String> multied = forHash.multiGet(RedisKey.IP_AUDIT_SWITCH, keys);
		for (int i = 0; i < keys.size(); i++) {
			map.put(WesKit.valueOf(keys.get(i)), AuditEnvConfigDTO.decode(multied.get(i)));
		}
		return map;
	}

	/** 指示当前客户端版本是否支持新版付费模式 */
	public static boolean supportsNewPayMode(String appId) {
		final String version = ProxyRequest.getVersion(appId);
		return ge(version, v_4_0_0);
	}

	/** 指示当前客户端版本是否支持新版付费模式 */
	public static boolean supportsNewPayMode(HttpServletRequest request) {
		return supportsNewPayMode(ProxyRequest.getAppId(request));
	}

	static final Map<String, Integer[]> versionMap = Map.of(
			"3.1.0", v_3_1_0,
			"3.3.0", v_3_3_0,
			"3.5.0", v_3_5_0,
			"3.5.1", v_3_5_1,
			"4.0.0", v_4_0_0,
			"4.2.0", v_4_2_0,
			"4.3.0", v_4_3_0,
			"4.4.0", v_4_4_0,
			"4.4.2", v_4_4_2,
			"5.3.5", v_5_3_5

			);

	/**
	 * 将版本号转为分段的 整数 或 字符串数组
	 */
	public static Object[] version2Parts(String version) {
		if (StringUtil.notEmpty(version)) {
			final Integer[] versions = versionMap.get(version);
			if (versions != null) {
				return versions;
			}
			return version.split("\\.");
		}
		return ArrayUtils.EMPTY_INTEGER_OBJECT_ARRAY;
	}

	/**
	 * 比较版本号
	 */
	public static int compare(String version1, String version2) {
		return compare(version2Parts(version1), version2Parts(version2));
	}

	/**
	 * 比较版本号
	 */
	public static int compare(String version1, Object[] version2Parts) {
		return compare(version2Parts(version1), version2Parts);
	}

	/**
	 * 比较版本号
	 */
	public static int compare(Object[] version1Parts, Object[] version2Parts) {
		return Common.compareVersions(version1Parts, version2Parts);
	}

}
